<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 动态背景</title>
    <link rel="shortcut icon" href="./images/icon.png" type="image/x-icon">
    <link rel="stylesheet" href="./css/canvasBg.css">
</head>

<body>
    <div class="wrapper">

        <div class="inputBox">
            点数：<input class="nodeNum" type="number" v-model="nodeNum" v-on:input="changeNodeNum" />
        </div>
        <div class="inputBox two">
            速度：<input class="speed" type="number" v-model="speed" v-on:input="changeSpeed" />
        </div>
        <div class="inputBox thr">
            版本：<span :class="['type',type==1?'active':'']" @click="type=1">正常</span><span
                :class="['type',type==2?'active':'']" @click="type=2">鬼畜</span>
        </div>
        <div class="inputBox four"><a href="https://www.dacongyaa.com/">开发者：行走的大葱鸭</a></div>
        <div v-show="showTip" class="loadTip">
            <div class="loading">
                <div class="cover"></div>
            </div>
            <span>{{tip}}</span>
        </div>
    </div>
    <canvas height="300" width="1360" id="canvas"
        style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;"></canvas>
    <script src="./js/vue.min.js"></script>
    <script>
        var canvasEl = document.getElementById("canvas");
        var ctx = canvasEl.getContext("2d");
        var type = 1;
        // 鼠标位置
        var mousePos = {
            x: null,
            y: null,
        };

        // 点节点
        var nodes = [];
        // 线条
        var edges = [];

        var speed = 1;
        var nodeNum = 100;

        window.onresize = function () {
            canvasEl.width = document.body.clientWidth;
            canvasEl.height = document.body.clientHeight;

            mousePos.x = Math.random() * canvasEl.width;
            mousePos.y = Math.random() * canvasEl.height;
        };

        window.onresize();

        window.onmousemove = function (e) {
            mousePos.x = e.clientX;
            mousePos.y = e.clientY;

            adjustNodeDrivenByMouse();
        };

        // 创建节点 以及 各个点之间的线条
        function createNode() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    for (var i = 0; i < parseInt(nodeNum) + 1; i++) {
                        var node = {
                            drivenByMouse: i == 0,
                            x: Math.random() * canvasEl.width,
                            y: Math.random() * canvasEl.height,
                            vx: Math.random() * speed - 0.1,
                            vy: Math.random() * speed - 0.1,
                            radius: Math.random() > 0.9 ?
                                3 + Math.random() * 3 : 1 + Math.random() * 3,
                        };

                        nodes.push(node);
                    }

                    // 创建各个点之间所有的线条数据
                    nodes.forEach(function (e) {
                        nodes.forEach(function (e2) {
                            if (e == e2) {
                                return;
                            }
                            var edge = {
                                from: e,
                                to: e2,
                            };
                            saveEdge(edge);
                        });
                    });
                    resolve();
                }, 60)
            })
        }
        createNode();

        // 保存各个点之间的线条数据 （需要做去重处理）
        function saveEdge(edge) {
            var ignore = false;
            edges.forEach(function (e) {
                if (e.from == edge.from && e.to == edge.to) {
                    ignore = true;
                }

                if (e.to == edge.from && e.from == edge.to) {
                    ignore = true;
                }
            });
            if (!ignore) {
                edges.push(edge);
            }
        }

        // 点的运动 - 加入边界值的判断
        function nodeMove() {
            nodes.forEach(function (e) {
                if (e.drivenByMouse) {
                    return;
                }

                e.x += e.vx;
                e.y += e.vy;

                function clamp(min, max, value) {
                    if (value > max) {
                        return max;
                    } else if (value < min) {
                        return min;
                    } else {
                        return value;
                    }
                }

                var l = lengthOfNodes(e);

                var threshold = canvasEl.width / 8;

                if (l > threshold) {
                    if (e.x <= 0 || e.x >= canvasEl.width) {
                        e.vx *= -1;
                        e.x = clamp(0, canvasEl.width, e.x);
                    }

                    if (e.y <= 0 || e.y >= canvasEl.height) {
                        e.vy *= -1;
                        e.y = clamp(0, canvasEl.height, e.y);
                    }
                } else {

                    if (
                        (e.x > mousePos.x && e.vx > 0) ||
                        (e.x < mousePos.x && e.vx < 0)
                    ) {
                        e.vx *= -1;
                    }
                    if (
                        (e.y > mousePos.y && e.vy > 0) ||
                        (e.y < mousePos.y && e.vy < 0)
                    ) {
                        e.vy *= -1;
                    }

                }
            });

            // 重复调用render进行渲染
            render();
            window.requestAnimationFrame(nodeMove);
        }

        // 获取每条线的长度用来判断是否要绘制
        function lengthOfEdge(edge) {
            // c = 根号 a平方 + b平方
            return Math.sqrt(
                Math.pow(edge.from.x - edge.to.x, 2) +
                Math.pow(edge.from.y - edge.to.y, 2)
            );
        }

        // 判断某个点距离鼠标的位置
        function lengthOfNodes(node) {
            if (mousePos.x && mousePos.y) {
                return Math.sqrt(
                    Math.pow(node.x - mousePos.x, 2) + Math.pow(node.y - mousePos.y, 2)
                );
            } else {
                return canvasEl.width / 4;
            }
        }

        // 让第一个不显示的点跟随鼠标移动 速度缓慢
        function adjustNodeDrivenByMouse() {
            nodes[0].x += (mousePos.x - nodes[0].x) / 5;
            nodes[0].y += (mousePos.y - nodes[0].y) / 5;
        }

        // 渲染
        function render() {

            // 绘制黑色背景的画布
            ctx.fillStyle = "#022158";
            ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);

            // 绘制节点
            nodes.forEach(function (e) {
                if (e.drivenByMouse) {
                    return;
                }

                ctx.fillStyle = type == 1 ? '#fff' : "rgba(" + (Math.random() * 255) + "," + (Math.random() *
                    255) + "," + (Math.random() * 255) + ")";
                ctx.beginPath();
                ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);
                ctx.fill();
            });

            // 绘制线条
            edges.forEach(function (e) {
                var l = lengthOfEdge(e);
                var threshold = canvasEl.width / 8;

                if (l > threshold) {
                    return;
                }
                ctx.strokeStyle = type == 1 ? '#fff' : "rgba(" + (Math.random() * 255) + "," + (Math.random() *
                    255) + "," + (Math.random() * 255) + ")";
                ctx.lineWidth = (1.0 - l / threshold) * 2.5;
                ctx.globalAlpha = 1.0 - l / threshold;
                ctx.beginPath();
                ctx.moveTo(e.from.x, e.from.y);
                ctx.lineTo(e.to.x, e.to.y);
                ctx.stroke();
            });
            ctx.globalAlpha = 1.0;
        }



        var app = new Vue({
            el: '.wrapper',
            data: {
                showTip: false,
                tip: "请输入合理数值",
                nodeNum: 100,
                speed: 1,
                type: 1
            },
            created() {
                this.start();
            },
            watch: {
                async type(val) {
                    type = val;
                    nodes = [];
                    edges = [];
                    await this.showTipBox("版本切换中...");
                    await createNode();
                    this.hideTipBox();
                }
            },
            methods: {
                async changeNodeNum() {
                    if (this.nodeNum >= 1 && this.nodeNum <= 200) {
                        nodeNum = this.nodeNum;
                        nodes = [];
                        edges = [];
                        await this.showTipBox("生成中...");
                        await createNode();
                        this.hideTipBox();
                    } else {
                        this.showTipBox("请输入 1-200 的值");
                        this.hideTipBoxByTime(1500);
                    }
                },
                async changeSpeed() {
                    if (this.speed >= 1 && this.speed <= 10) {
                        nodes = [];
                        edges = [];
                        speed = this.speed;
                        await this.showTipBox("生成中...");
                        await createNode();
                        this.hideTipBox();
                    } else {
                        this.showTipBox("请输入 1-10 的值");
                        this.hideTipBoxByTime(1500);
                    }
                },
                // 显示提示
                async showTipBox(txt) {
                    let _this = this;
                    return new Promise((resolve, reject) => {
                        _this.showTip = true;
                        _this.tip = txt;
                        setTimeout(() => {
                            resolve();
                        })
                    })

                },
                hideTipBox() {
                    this.showTip = false;
                    this.tip = '';
                },
                hideTipBoxByTime(time) {
                    setTimeout(() => {
                        this.showTip = false;
                        this.tip = '';
                    }, time)
                },
                start() {
                    render();
                    window.requestAnimationFrame(nodeMove);
                }
            }
        })
    </script>
</body>

</html>